/*
 * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available.
 *
 * Copyright (C) 2022 Tencent.  All rights reserved.
 *
 * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license.
 *
 * A copy of the MIT License is included in this file.
 *
 *
 * Terms of the MIT License:
 * ---------------------------------------------------
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
 * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of
 * the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
 * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
 * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

package com.tencent.bkrepo.job.batch.task.stat

import com.tencent.bkrepo.common.api.constant.StringPool
import com.tencent.bkrepo.job.batch.base.ActiveProjectService
import com.tencent.bkrepo.job.batch.base.JobContext
import com.tencent.bkrepo.job.batch.context.EmptyFolderCleanupJobContext
import com.tencent.bkrepo.job.batch.utils.FolderUtils
import com.tencent.bkrepo.job.config.properties.ActiveProjectEmptyFolderCleanupJobProperties
import com.tencent.bkrepo.job.pojo.stat.StatNode
import com.tencent.bkrepo.job.separation.service.SeparationTaskService
import org.slf4j.LoggerFactory
import org.springframework.data.mongodb.core.MongoTemplate
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor
import org.springframework.stereotype.Component
import java.time.Duration


/**
 * 空目录清理job
 */
@Component
class ActiveProjectEmptyFolderCleanupJob(
    val properties: ActiveProjectEmptyFolderCleanupJobProperties,
    executor: ThreadPoolTaskExecutor,
    private val activeProjectService: ActiveProjectService,
    private val mongoTemplate: MongoTemplate,
    private val emptyFolderCleanup: EmptyFolderCleanup,
    private val separationTaskService: SeparationTaskService,
) : StatBaseJob(mongoTemplate, properties, executor, separationTaskService) {

    override fun doStart0(jobContext: JobContext) {
        logger.info("start to do empty folder cleanup job for active projects")
        require(jobContext is EmptyFolderCleanupJobContext)
        doStatStart(jobContext, jobContext.activeProjects.keys)
        logger.info("empty folder cleanup job for active projects finished")
    }


    override fun beforeRunProject(projectId: String) {
        if (properties.userMemory) return
        // 每次任务启动前要将redis上对应的key清理， 避免干扰
        val key = KEY_PREFIX + StringPool.COLON +
            FolderUtils.buildCacheKey(projectId = projectId, repoName = StringPool.EMPTY)
        emptyFolderCleanup.removeRedisKey(key)
    }

    override fun runRow(row: StatNode, context: JobContext) {
        require(context is EmptyFolderCleanupJobContext)
        try {
            val node = emptyFolderCleanup.buildNode(
                id = row.id,
                projectId = row.projectId,
                repoName = row.repoName,
                path = row.path,
                fullPath = row.fullPath,
                folder = row.folder,
                size = row.size
            )
            emptyFolderCleanup.collectEmptyFolderWithMemory(
                row = node,
                context = context,
                keyPrefix = KEY_PREFIX,
                useMemory = properties.userMemory,
                cacheNumLimit = properties.cacheNumLimit
            )
        } catch (e: Exception) {
            logger.error("run empty folder clean for Node $row failed, ${e.message}")
        }
    }

    override fun getLockAtMostFor(): Duration {
        return Duration.ofDays(14)
    }

    override fun createJobContext(): EmptyFolderCleanupJobContext {
        val temp = mutableMapOf<String, Boolean>()
        activeProjectService.getActiveProjects().forEach {
            temp[it] = true
        }
        return EmptyFolderCleanupJobContext(
            activeProjects = temp
        )
    }

    override fun onRunProjectFinished(collection: String, projectId: String, context: JobContext) {
        require(context is EmptyFolderCleanupJobContext)
        logger.info("will filter empty folder in project $projectId")

        if (!properties.userMemory) {
            emptyFolderCleanup.collectEmptyFolderWithRedis(
                context = context,
                force = true,
                keyPrefix = KEY_PREFIX,
                collectionName = null,
                projectId = projectId,
                cacheNumLimit = properties.cacheNumLimit
            )
        }
        if (properties.userMemory) {
            emptyFolderCleanup.emptyFolderHandlerWithMemory(
                collection = collection,
                context = context,
                deletedEmptyFolder = properties.deletedEmptyFolder,
                projectId = projectId,
                deleteFolderRepos = properties.deleteFolderRepos
            )
        } else {
            emptyFolderCleanup.emptyFolderHandlerWithRedis(
                collection = collection,
                keyPrefix = KEY_PREFIX,
                context = context,
                deletedEmptyFolder = properties.deletedEmptyFolder,
                projectId = projectId,
                deleteFolderRepos = properties.deleteFolderRepos
            )
        }
    }

    companion object {
        private val logger = LoggerFactory.getLogger(ActiveProjectEmptyFolderCleanupJob::class.java)
        private const val KEY_PREFIX = "activeEmptyFolder"
    }
}
